import cv2
import tkinter as tk
from tkinter import ttk
from PIL import Image, ImageTk
import numpy as np
import os
def face_gui():
    root = tk.Tk()
    root.title("人脸识别系统软件")
    #root.iconbitmap('face.ico')
    root.geometry("850x500")
    #加载训练数据集文件
    #recogizer=cv2.face.LBPHFaceRecognizer_create()
    #recogizer.read('trainer/trainer.yml')
    #图片路径
    global path
    path='./data/'
    camera_port = tk.StringVar()
    camera_port.set("2")  # 设置默认相机端口为0

    resolution = tk.StringVar()
    resolution.set("720p")  # 设置默认分辨率为480p

    haarcascade = tk.StringVar()
    haarcascade.set("models/haarcascade_frontalface_default.xml")  # 设置默认haarcascade模型

    # 创建选项卡式窗口
    notebook = ttk.Notebook(root)
    notebook.pack(fill='both', expand=True)

    # 创建人脸识别界面
    camera_page = ttk.Frame(notebook)
    notebook.add(camera_page, text="人脸识别界面")


    # 创建图像显示区域
    canvas = tk.Canvas(camera_page,height=300,width=800)
    canvas.grid(row=5, column=0, rowspan=1, columnspan=6)
    color_label = tk.Label(canvas)
    depth_label = tk.Label(canvas)
    canvas.create_rectangle(2, 2, 399, 299)
    canvas.create_rectangle(400, 2, 799, 299)
    canvas.create_window(200, 150, window=color_label)
    canvas.create_window(600, 150, window=depth_label)

    def open_camera():
        # 从相机读取图像
        global cap
        camera_ports = camera_port.get()
        if camera_ports == "":
            camera_ports = 0  # 如果相机端口为空字符串，则设置为默认端口0
        else:
            camera_ports = int(camera_ports)
        cap = cv2.VideoCapture(camera_ports)

        resolutions = resolution.get()
        match resolutions:
            case "720p":
                cap.set(cv2.CAP_PROP_FRAME_WIDTH, 1024)
                cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 768)
            case "480p":
                cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640)
                cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 480)
            case "360p":
                cap.set(cv2.CAP_PROP_FRAME_WIDTH, 320)
                cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 240)
        ret, frame = cap.read()
        try:
            while ret:
                ret, frame = cap.read()
                # 在图像上进行人脸检测
                gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
                cascade = cv2.CascadeClassifier(haarcascade.get())
                faces = cascade.detectMultiScale(gray, scaleFactor=1.1, minNeighbors=5, minSize=(30, 30))

                current_image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
                pil_current_image = Image.fromarray(current_image)
                # 在图像上绘制人脸框并显示
                for (x, y, w, h) in faces:
                    cv2.rectangle(frame, (x, y), (x+w, y+h), (0, 255, 0), 2)

                # 将OpenCV图像转换为PIL图像
                face_image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
                pil_face_image = Image.fromarray(face_image)

                # 将PIL图像转换为Tkinter图像
                tk_current_image = ImageTk.PhotoImage(pil_current_image.resize((380, 285)))
                tk_face_image = ImageTk.PhotoImage(pil_face_image.resize((380, 285)))

                # 更新图像显示
                color_label.config(image=tk_current_image)
                depth_label.config(image=tk_face_image)

                # 保持图像显示的引用，防止被垃圾回收
                color_label.image = pil_current_image
                depth_label.image = pil_face_image

                # 每隔100毫秒更新图像显示
                camera_page.update()
        finally:
            cap.release()

    # 创建保存图像函数
    def save_image(color_image):
        # 创建data文件夹（如果不存在）
        if not os.path.exists("data"):
            os.makedirs("data")

        # 获取data文件夹下已保存图像的数量
        # num_files = len(os.listdir("data"))

            # 获取data文件夹下最大的序号
        num_files = 0
        for file_name in os.listdir("./data"):
            if file_name.endswith(".png"):
                index = int(file_name.split(".")[0])
                num_files = max(num_files, index)

        # 保存彩色图为PNG格式
        color_image_path = "data/{:04d}.png".format(num_files + 1)
        color_image.save(color_image_path)
        print(f"Color image saved as {color_image_path}")

    def save_button_callback():
        # 获取当前显示的彩色图和深度图
        color_image = color_label.image
        # 保存当前图像
        if color_image is not None:
            save_image(color_image)
        else:
            print("No image to save")

    def close_camera():
        # camera_static = 1
        cap.release()


    # 创建相机端口选择菜单
    camera_label = tk.Label(camera_page, text="相机  端口:")
    camera_label.grid(row=1, column=1)

    camera_menu = ttk.Combobox(camera_page, textvariable=camera_port, value=("0", "1", "2"))
    camera_menu.configure(width=100)
    camera_menu.grid(row=1, column=3, columnspan=1)

    # 创建分辨率选择菜单
    resolution_label = tk.Label(camera_page, text="图像分辨率:")
    resolution_label.grid(row=2, column=1)

    resolution_menu = ttk.Combobox(camera_page, textvariable=resolution, value=("720p", "480p", "360p"))
    resolution_menu.configure(width=100)
    resolution_menu.grid(row=2, column=3, columnspan=1)

    # 创建haarcascade模型选择菜单
    haarcascade_label = tk.Label(camera_page, text="Haar 模型:")
    haarcascade_label.grid(row=3, column=1)

    haarcascade_menu = ttk.Combobox(camera_page, textvariable=haarcascade, value=("models/haarcascade_frontalface_alt.xml", "models/haarcascade_frontalface_alt2.xml", "models/haarcascade_frontalface_default.xml"))
    haarcascade_menu.configure(width=100)
    haarcascade_menu.grid(row=3, column=3, columnspan=1)

    # 创建打开相机按钮
    open_button = tk.Button(camera_page, text="打开相机", command=open_camera, bg='green',fg='yellow')
    open_button.grid(row=4, column=1)

    # 创建关闭相机按钮
    close_button = tk.Button(camera_page, text="关闭相机", command=close_camera, bg='red',fg='yellow')
    close_button.grid(row=4, column=3)

    # 创建保存图像按钮
    save_button = tk.Button(camera_page, text="保存图像", command=save_button_callback, bg='blue',fg='yellow')
    save_button.grid(row=4, column=5)

    la2=tk.Label(camera_page,text='原始图像')
    la2.grid(row=6,column=1) # 0行0列

    la2=tk.Label(camera_page,text='识别图像')
    la2.grid(row=6,column=5) # 0行0列

    # 创建人脸登记界面
    model_page = ttk.Frame(notebook)
    notebook.add(model_page, text="人脸登记界面")

    # 创建图像显示区域
    canvas1 = tk.Canvas(model_page,height=300,width=800)
    canvas1.grid(row=5, column=0, rowspan=1, columnspan=6)
    color_label1 = tk.Label(canvas1)
    depth_label1 = tk.Label(canvas1)
    canvas1.create_rectangle(2, 2, 399, 299)
    canvas1.create_rectangle(400, 2, 799, 299)
    canvas1.create_window(200, 150, window=color_label1)
    canvas1.create_window(600, 150, window=depth_label1)

    def open_camera1():
        # 从相机读取图像
        # cap.release()
        global cap1
        camera_ports = camera_port.get()
        if camera_ports == "":
            camera_ports = 0  # 如果相机端口为空字符串，则设置为默认端口0
        else:
            camera_ports = int(camera_ports)
        cap1 = cv2.VideoCapture(camera_ports)

        cap1.set(cv2.CAP_PROP_FRAME_WIDTH, 1024)
        cap1.set(cv2.CAP_PROP_FRAME_HEIGHT, 768)

        ret, frame = cap1.read()
        try:
            while ret:
                ret, frame = cap1.read()

                current_image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
                pil_current_image = Image.fromarray(current_image)

                # 将PIL图像转换为Tkinter图像
                tk_current_image = ImageTk.PhotoImage(pil_current_image.resize((380, 285)))

                # 更新图像显示
                color_label1.config(image=tk_current_image)

                # 保持图像显示的引用，防止被垃圾回收
                color_label1.image = pil_current_image

                # 每隔100毫秒更新图像显示
                model_page.update()
        finally:
            cap1.release()

    # 创建保存图像函数
    def save_name_image():
        ret_flag,Vshow = cap1.read()#得到每帧图像
        # 创建data文件夹（如果不存在）
        if not os.path.exists("data"):
            os.makedirs("data")

        # 获取data文件夹下已保存图像的数量
        # num_files = len(os.listdir("data"))

            # 获取data文件夹下最大的序号
        num_files = 0
        for file_name in os.listdir("./data"):
            if file_name.endswith(".jpg"):
                index = int(file_name.split(".")[0])
                num_files = max(num_files, index)

        # 保存彩色图为PNG格式
        cv2.imwrite("data/"+str(num_files + 1)+"."+ a_entry.get() +".jpg",Vshow)
        print("success to save"+str(num_files + 1)+".jpg")
        print("-------------------")



        # 创建相机端口选择菜单

    def getImageAndLabels(path):
        facesSamples=[]
        ids=[]
        imagePaths=[os.path.join(path,f) for f in os.listdir(path)]
        #检测人脸
        face_detector = cv2.CascadeClassifier('models/haarcascade_frontalface_alt2.xml')
        #打印数组imagePaths
        print('数据排列：',imagePaths)
        #遍历列表中的图片
        for imagePath in imagePaths:
            #打开图片,黑白化
            PIL_img=Image.open(imagePath).convert('L')
            #将图像转换为数组，以黑白深浅
        # PIL_img = cv2.resize(PIL_img, dsize=(400, 400))
            img_numpy=np.array(PIL_img,'uint8')
            #获取图片人脸特征
            faces = face_detector.detectMultiScale(img_numpy)
            #获取每张图片的id和姓名
            id = int(os.path.split(imagePath)[1].split('.')[0])
            #预防无面容照片
            for x,y,w,h in faces:
                ids.append(id)
                facesSamples.append(img_numpy[y:y+h,x:x+w])
            #打印脸部特征和id
            #print('fs:', facesSamples)
            print('id:', id)
            #print('fs:', facesSamples[id])
        print('fs:', facesSamples)
        #print('脸部例子：',facesSamples[0])
        #print('身份信息：',ids[0])
        return facesSamples,ids

    def train_face():

        #获取图像数组和id标签数组和姓名
        faces,ids=getImageAndLabels(path)
        #获取训练对象
        recognizer=cv2.face.LBPHFaceRecognizer_create()
        #recognizer.train(faces,names)#np.array(ids)
        recognizer.train(faces,np.array(ids))
        #保存文件
        recognizer.write('trainer/trainer.yml')

    #准备识别的图片
    def face_detect_demo():
        # 从相机读取图像
        global cap1
        camera_ports = camera_port.get()
        if camera_ports == "":
            camera_ports = 0  # 如果相机端口为空字符串，则设置为默认端口0
        else:
            camera_ports = int(camera_ports)
        cap1 = cv2.VideoCapture(camera_ports)
        names = name()
        cap1.set(cv2.CAP_PROP_FRAME_WIDTH, 1024)
        cap1.set(cv2.CAP_PROP_FRAME_HEIGHT, 768)
        ret, frame = cap1.read()
        try:
            while ret:
                ret, frame = cap1.read()
                # 在图像上进行人脸检测
                gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
                current_image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
                pil_current_image = Image.fromarray(current_image)
                face_detector=cv2.CascadeClassifier('models/haarcascade_frontalface_alt2.xml')
                face=face_detector.detectMultiScale(gray,1.1,5,cv2.CASCADE_SCALE_IMAGE,(100,100),(300,300))
                #face=face_detector.detectMultiScale(gray)
                global warningtime
                warningtime = 0
                for x,y,w,h in face:
                    cv2.rectangle(frame,(x,y),(x+w,y+h),color=(0,0,255),thickness=2)
                    cv2.circle(frame,center=(x+w//2,y+h//2),radius=w//2,color=(0,255,0),thickness=1)
                    # 人脸识别
                    ids, confidence = recogizer.predict(gray[y:y + h, x:x + w])
                    #print('标签id:',ids,'置信评分：', confidence)
                    if confidence > 80:

                        warningtime += 1
                        if warningtime > 100:
                            # warning()
                            warningtime = 0
                        cv2.putText(frame, 'unkonw', (x + 10, y - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.75, (0, 255, 0), 1)
                    else:
                        cv2.putText(frame,str(names[ids-1]+"{:.1f}".format(confidence)), (x + 10, y - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.75, (0, 255, 0), 1)
                # 将OpenCV图像转换为PIL图像
                face_image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
                pil_face_image = Image.fromarray(face_image)

                # 将PIL图像转换为Tkinter图像
                tk_current_image = ImageTk.PhotoImage(pil_current_image.resize((380, 285)))
                tk_face_image = ImageTk.PhotoImage(pil_face_image.resize((380, 285)))

                # 更新图像显示
                color_label1.config(image=tk_current_image)
                depth_label1.config(image=tk_face_image)

                # 保持图像显示的引用，防止被垃圾回收
                color_label1.image = pil_current_image
                depth_label1.image = pil_face_image

                # 每隔100毫秒更新图像显示
                camera_page.update()
        finally:
            cap1.release()

    def name():
        names=[]
        imagePaths=[os.path.join(path,f) for f in os.listdir(path)]
        for imagePath in imagePaths:
            name = str(os.path.split(imagePath)[1].split('.',2)[1])
            names.append(name)
        return names

    def close_camera1():
        cap1.release()

    camera_label = tk.Label(model_page, text="相机  端口:")
    camera_label.grid(row=1, column=1)

    camera_menu = ttk.Combobox(model_page, textvariable=camera_port, value=("0", "1", "2"))
    camera_menu.configure(width=50)
    camera_menu.grid(row=1, column=3, columnspan=1)

    addr = tk.StringVar(value='lp')
    a_label = tk.Label(model_page, text = "⽤户名：")
    a_label.grid(row = 2, column = 1)
    a_entry = tk.Entry(model_page, textvariable=addr)
    a_entry.grid(row = 2, column = 3)
    # 创建打开相机按钮
    open1_button = tk.Button(model_page, text="打开相机", command=open_camera1, bg='green',fg='yellow')
    open1_button.grid(row=4, column=2)

    # 创建关闭相机按钮
    close_button = tk.Button(model_page, text="关闭相机", command=close_camera1, bg='red',fg='yellow')
    close_button.grid(row=4, column=3)

    # 创建保存图像按钮
    save_button = tk.Button(model_page, text="保存图像", command=save_name_image, bg='blue',fg='yellow')
    save_button.grid(row=4, column=4)

    # 创建训练特定人脸按钮
    #train_button = tk.Button(model_page, text="训练特定人脸", command=train_face, bg='red',fg='yellow')
    #train_button.grid(row=6, column=2)

    # 创建识别特定人脸按钮
    #train_button = tk.Button(model_page, text="识别特定人脸", command=face_detect_demo, bg='red',fg='yellow')
    #train_button.grid(row=6, column=4)

    # # 创建更新人脸数据表格按钮
    # update_button = tk.Button(model_page, text="更新人脸数据表格", command=update_excel, bg='blue',fg='yellow')
    # update_button.grid(row=6, column=4)
    # 创建人脸数据界面
    face_data_page = ttk.Frame(notebook)
    notebook.add(face_data_page, text="人脸数据界面")

    tree = ttk.Treeview(face_data_page)      # #创建表格对象
    # canvas1.create_window(600, 150, window=tree)


    tree["columns"] = ("序号","姓名", "数量")     # #定义列
    tree.column("序号", width=80)          # #设置列
    tree.column("姓名", width=80)
    tree.column("数量", width=80)
    tree.heading("序号", text="x序号")
    tree.heading("姓名", text="x姓名")        # #设置显示的表头名
    tree.heading("数量", text="x数量")

    names = name()
    # imagePaths=[os.path.join(path,f) for f in os.listdir(path)]
    # for imagePath in imagePaths:
    #     name = str(os.path.split(imagePath)[1].split('.',2)[1])
    #     names.append(name)
    names = list(set(names))#去重
    number = 0
    for i in range(0,len(names)):
        for file in os.listdir(path):
            if file.endswith(str(names[i]+".jpg")):
                number += 1 # 统计当前文件夹下csv类型文件的总数
        tree.insert("", 0, text="line"+str(i), values=(str(i+1), names[i], str(number)))
        i = i + 1
        number = 0
    tree.grid()
    # 创建拍照保存按钮
    button_frame = ttk.Frame(root)
    button_frame.pack(side='bottom', fill='x')
    quit_button = ttk.Button(button_frame, text="Quit", command=root.quit)
    quit_button.pack(side='right', padx=5, pady=5)
    root.mainloop()

if __name__ == '__main__':
    face_gui()
